package org.suze.demo.route;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.util.UrlPathHelper;
import org.suze.demo.annotation.NoRouting;
import org.suze.framework.base.Result;
import org.suze.framework.base.enums.RemoteEnum;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

/**
 * 数据源设置拦截器
 */
@Aspect
@Component
@Slf4j
public class DataSourceAdvice {

    /**
     * 配置排除拦截的地址列表, 建议使用 {@link NoRouting}
     */
    @Value("${routing.excludePatterns:}")
    private String excludePatterns;

    private UrlPathHelper urlPathHelper = new UrlPathHelper();

    /**
     * 切入点设置
     */
    @Pointcut("execution(* org.suze.demo.controller..*(..))")
    public void dataSource(){

    }

    /**
     * 拦截指定切入点，在方法执行之前设置数据源，在方法结束，重置数据源
     * @param joinPoint
     */
    @Around("dataSource()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Object proceedResult ;
        String target = null;
        String systemSource = null;
        Object[] args ;
        try {
            try {
                Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
                Class<?> targetClass = joinPoint.getTarget().getClass();
                String className = targetClass.getName();
                target = className + "." + method.getName();
                args = joinPoint.getArgs();
                NoRouting annotation = method.getAnnotation(NoRouting.class);
                if(annotation == null) {
                    RequestAttributes ra = RequestContextHolder.getRequestAttributes();
                    ServletRequestAttributes sra = (ServletRequestAttributes) ra;
                    HttpServletRequest request = sra.getRequest();
                    String lookupPathForRequest = urlPathHelper.getLookupPathForRequest(request);
                    // 通过配置禁用数据源路由检测
                    boolean routing = true;
                    if(null != excludePatterns && !"".equals(excludePatterns) && excludePatterns.length() > 0) {
                        List<String> configMapping = Arrays.asList(excludePatterns.split(";"));
                        for (String mapping : configMapping) {
                            AntPathMatcher antPathMatcher = new AntPathMatcher();
                            if (antPathMatcher.match(mapping, lookupPathForRequest)) {
                                routing = false;
                                break;
                            }
                        }
                    }
                    if(routing) {
                        DynamicDataSourceHandler.set(DataSourceEnum.DEFAULT);
                    }
                }
            } catch (Exception e) {
                log.error("datasource advice error, target={}", target, e);
                return Result.fail(RemoteEnum.ERROR_IN_SERVER);
            }
            log.info("datasource advice routing, datasource={}, target={}, systemSource={}, arg={}", DynamicDataSourceHandler.get(), target, JSON.toJSONString(systemSource), JSON.toJSONString(args));
            proceedResult = joinPoint.proceed();
        } finally {
            DynamicDataSourceHandler.reset();
        }
        return proceedResult;
    }

}
